home *** CD-ROM | disk | FTP | other *** search
/ AppleScript - The Beta Release / AppleScript - The Beta Release.iso / Documentation / develop / Apple Event Objects and You / Apple Event Objects (code) / ScrapTools.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-08  |  15.8 KB  |  478 lines  |  [TEXT/KAHL]

  1. /*  File: ScrapTools.c */
  2.  
  3. #include <AppleEvents.h>
  4. #include "AERegistry.h"
  5. #include "AEObjects.h"
  6.  
  7. #include "ScriptScrap.h"
  8. #include "Prototypes.h"
  9.  
  10.  
  11.  
  12. OSErr _EntryToScrapIndex(short entryNum, short *scrapIndex, short *resID)
  13. {
  14.     smapHand    theSmap;
  15.     short        count, entriesSeen;
  16.     OSErr        err;
  17.     
  18.     /* The scrapbook map consists of an array of bytes, where the value of    */
  19.     /* each byte is either 0 (empty) or the number of some entry in the        */
  20.     /* scrapbook. (for example, the SMAP might begin with "[2, 0, 1, 0, …"    */
  21.     /* which indicates that the the first entry in the SMAP corresponds     */
  22.     /* with the second entry in the scrapbook, and the third entry in the     */
  23.     /* SMAP corresponds with the first entry in the scrapbook.)             */
  24.     
  25.     /* Furthermore, each entry in the SMAP corresponds with the resource    */
  26.     /* number for the items that comprise that entry: SMAP[0] = resource ID    */
  27.     /* (-32768), SMAP[1] = resource(-32767), SMAP[2] = resource(-32766),    */
  28.     /* and so on.  Therefore, we can get the resource ID for an item using    */
  29.     /* the following algorithm:                                                */
  30.     /* 1) Scan the table to find the entry whose value == entryNum            */
  31.     /* 2) resID == scrapIndex - 32768                                        */
  32.     
  33.     theSmap = (smapHand)Get1Resource('SMAP', 0);
  34.     if (theSmap == 0L) return resNotFound;
  35.     if ((err = ResError()) != noErr) return err;
  36.     
  37.     /* Convert the entry number into a scrap index */
  38.     for (count = 0; count <= 255; count++)
  39.         if ((**theSmap)[count] == entryNum) {
  40.             /* We found it */
  41.             *scrapIndex = count;
  42.             *resID = count - 32768L;
  43.             return noErr;
  44.         }
  45.     
  46.     return errAENotAnElement;    /* If we didn't find anything, exit */
  47. }
  48.  
  49.  
  50. OSErr _Get1ScrapItem(short resID, OSType itemType, AEDesc *item)
  51. /* Extract an entry from the scrapbook and put the result into an AEDesc */
  52. /* whose handle is a copy of the resource's handle and whose type == the */
  53. /* the resource's type.                                                     */
  54. {
  55.     OSErr    err;
  56.     
  57.     item->dataHandle = Get1Resource(itemType, resID);
  58.     if (item->dataHandle == 0L) return resNotFound;
  59.     if ((err = ResError()) != noErr) return err;
  60.     item->descriptorType = itemType;
  61.     HandToHand(&item->dataHandle);        /* Copy the handle so we can close the resource */
  62.                                         /* file or dispose of the AEDesc without a problem.    */
  63.     if ((err = MemError()) != noErr) {
  64.         DisposHandle(item->dataHandle);
  65.         item->dataHandle = 0L;
  66.     }
  67.     return err;
  68. } /* _Get1ScrapItem */
  69.  
  70.  
  71. OSErr CountScrapbookEntries(short *count)
  72. {
  73.     smapHand    theSmap;
  74.     char        *entryPtr;
  75.     OSErr        err;
  76.     short        index;
  77.     
  78.     *count = 0;
  79.     
  80.     theSmap = (smapHand)Get1Resource('SMAP', 0);    /* The SMAP is the index to the resources     */
  81.     if (theSmap == 0L) return resNotFound;            /* contained within the scrapbook file.     */
  82.     if ((err = ResError()) != noErr) return err;
  83.     
  84.     /* Count the entries */
  85.     for (entryPtr = (char*)*theSmap, index = 0; index <= 255; index++, entryPtr++)
  86.         if (*entryPtr != '\0') (*count)++;
  87.     
  88.     return noErr;
  89. }
  90.  
  91.  
  92. OSErr Get1ScrapbookItem (short entryIndex, ResType itemType, AEDesc *item)
  93. {
  94.     /* This routine extracts all of the items of a given type from a scrapbook    */
  95.     /* entry. If itemType = typeWildCard then all of the items are extracted &     */
  96.     /* made into a list.                                                         */
  97.     /* This routine stops if an error occurs and returns all of the entries     */
  98.     /* collected to that point.                                                    */
  99.     
  100.     AEDesc        oneDesc;
  101.     ResType        oneType;
  102.     short        resID, smapIndex, numTypes, typeIndex, listIndex;
  103.     OSErr        err;
  104.         
  105.     /* Initialize the returned item, in case an error occurs */
  106.     item->dataHandle = 0L;
  107.     item->descriptorType = '    ';
  108.     
  109.     err = _EntryToScrapIndex(entryIndex, &smapIndex, &resID);
  110.     if (err != noErr) return err;
  111.  
  112.     /* If we've been asked for a specific type, look for it */
  113.     if (itemType != typeWildCard)
  114.         return _Get1ScrapItem(resID, itemType, item);
  115.     else {
  116.         /* The user asked for everything in this item, so we'll look through all */
  117.         /* available types for items with this resource number.                     */
  118.         /* We have to do this because the scrapbook map gives no indication as      */
  119.         /* to which resource types are contained in a given scrapbook entry.     */
  120.         numTypes = CountTypes();
  121.         for (typeIndex = 1, listIndex = 1; typeIndex <= numTypes; typeIndex++) {
  122.             Get1IndType(&oneType, typeIndex);
  123.             if ((err = ResError()) != noErr) return err;
  124.             err = _Get1ScrapItem(resID, oneType, &oneDesc);
  125.             if (err == noErr) {
  126.                 /* We have an item in oneDesc, so add it to the list */
  127.                 if (item->dataHandle == 0L) {
  128.                     /* But first, create the list if it doesn't exist */
  129.                     err = AECreateList(0L, 0, true /* isRecord */, item);
  130.                      if (err != noErr) return err;
  131.                 }
  132.                 Debugger();
  133.                 err = AEPutDesc(item, listIndex, &oneDesc);
  134.                 listIndex++;
  135.                 (void) AEDisposeDesc(&oneDesc);            /* Get rid of the single entry whether */
  136.                                                         /* there was an error in AEPutDesc or not */
  137.                 if (err != noErr) return err;
  138.             }
  139.             /* Ignore "resNotFound" errors, since we are likely to get quite a few */
  140.             /* of them as we try and find the resources for this one entry */
  141.             if ((err != noErr) && (err != resNotFound)) return err;
  142.         }
  143.     }
  144.     return noErr;
  145. }    /* Get1ScrapbookEntry */
  146.  
  147.  
  148. OSErr Put1ScrapbookItem (short entryIndex, const AEDesc *entryContents)
  149. {
  150.     /* This routine takes one or more resources (packaged as AEDescs with the */
  151.     /* resource type in the descriptorType field and the resource handles in  */
  152.     /* the dataHandle field) and merges them into the specified entry.          */
  153.     
  154.     OSErr        err;
  155.     short        scrapIndex, listIndex, resID;
  156.     Handle        resHandle;
  157.     long        itemCount;
  158.     AEDesc        itemDesc;
  159.     AEKeyword    itemKeyword;
  160.     smapHand    theSmap;
  161.     char        *scrapbookMap;
  162.     AEDescList    entryList = {typeNull, NULL};
  163.     
  164.     /* Get the resource number for this scrapbook entry */
  165.     err = _EntryToScrapIndex(entryIndex, &scrapIndex, &resID);
  166.     if (err) goto done;
  167.     
  168.     /* Make sure that we have a list of items (since this routine can take either */
  169.     /* a single item or a list.                                                      */
  170.     err = AECoerceDesc(entryContents, typeAEList, &entryList);
  171.     if (err != noErr) goto done;
  172.     
  173.     /* 1. Find out how many items we need to insert into this entry */
  174.     err = AECountItems(&entryList, &itemCount);
  175.     if (err != noErr) goto done;
  176.     if (itemCount == 0) {
  177.         err = errAEEventFailed;    /* Can't continue if the list is empty */
  178.         goto done;
  179.     }
  180.     
  181.     /* 2. Add a resource for each of the list items, replacing any resources which may */
  182.     /*    already exist there. */
  183.     for (listIndex = 1; listIndex <= itemCount; listIndex++) {
  184.         err = AEGetNthDesc(&entryList, listIndex, typeWildCard, &itemKeyword, &itemDesc);
  185.         if (err != noErr) goto done;
  186.         /* remove any copies of this resource that may already exist */
  187.         do {
  188.             resHandle = Get1Resource(itemDesc.descriptorType, resID);
  189.             if (resHandle != 0L) {
  190.                 RmveResource(resHandle);
  191.                 if ((err = ResError()) != noErr) goto done;
  192.                 DisposHandle(resHandle);
  193.             }
  194.         } while (resHandle != 0L);
  195.         
  196.         /* Create a new resource */
  197.         AddResource(itemDesc.dataHandle, itemDesc.descriptorType, resID, "\p");
  198.         WriteResource(itemDesc.dataHandle);
  199.         if ((err = ResError()) != noErr) goto done;
  200.         ReleaseResource(itemDesc.dataHandle);        /* We don't need to keep this resource */
  201.                                                     /* in memory. */
  202.         /* We don't need to dispose of the AEDesc since the handle has been converted */
  203.         /* into a resource, and ReleaseResource disposed of it for us. */
  204.     }
  205.     
  206. done:
  207.     if (entryList.dataHandle != NULL)
  208.         (void) AEDisposeDesc(&entryList);
  209.     return err;
  210. }    /* Put1ScrapbookItem */
  211.  
  212.  
  213. OSErr InsertScrapbookEntry (short entryNum, const AEDescList *itemList)
  214. {
  215.     /* This adds a new entry to the scrapbook file before the entry specified by */
  216.     /* "entryNum". */
  217.     smapHand    theSmap;
  218.     char        *slotPtr;
  219.     short        slotNum, resID;
  220.     OSErr        err;
  221.     unsigned char slotMap[33] = {0}; /* Add 1 extra byte of zeroes at the end as a guard }
  222.     
  223.     /* Find an emply slot in the smap    */
  224.     theSmap = (smapHand)Get1Resource('SMAP', 0);
  225.     if (theSmap == 0L) return resNotFound;
  226.     if ((err = ResError()) != noErr) return err;
  227.     
  228.     /* Look for the first non-zero slot */
  229.     for (slotNum = 0, slotPtr = (char *)*theSmap;    /* Initial conditions */
  230.          (*slotPtr != '\0') && (slotNum <= 255);     /* test */
  231.          slotNum++, slotPtr++) ;                    /* repeat while test == true */
  232.  
  233.      if (slotNum > 255)
  234.         return errAEWriteDenied;    /* We're out of slots */
  235.     
  236.     
  237.     resID = slotNum - 32768L;        /* Calculate a resource ID */
  238.     
  239.     /* Adjust all of the subsequent entry numbers upwards and claim our slot */
  240.     err = AdjustScrapbookEntries(entryNum, true);
  241.     if (err != noErr) return err;
  242.     **theSmap[slotNum] = entryNum;
  243.     
  244.     /* Store the information we've been passed */
  245.     if (itemList->dataHandle != 0L)
  246.         err = Put1ScrapbookItem(entryNum, itemList);
  247.         
  248.     return err;
  249. }    /* InsertScrapbookEntry */
  250.  
  251.  
  252. OSErr _Delete1ScrapItem(short resID, OSType itemType)
  253. /* Remove a resource with a particular type and ID from the scrapbook file */
  254. {
  255.     OSErr    err = noErr;
  256.     Handle    resHandle;
  257.     
  258.     while (true) {
  259.         /* Remove each instance of this resource */
  260.         resHandle = Get1Resource(itemType, resID);
  261.         if (resHandle == 0L) break;
  262.         if ((err = ResError()) != noErr) break;
  263.         RmveResource(resHandle);
  264.         DisposHandle(resHandle);
  265.         if ((err = ResError()) != noErr) break;
  266.     };
  267.     return err;
  268. } /* _Delete1ScrapItem */
  269.  
  270.  
  271.  
  272. OSErr AdjustScrapbookEntries(short startValue, Boolean adjustUpwards)
  273. {
  274.     /* This is used to adjust entry numbers in the scrapbook when an entry is  */
  275.     /* inserted or deleted. If you want to insert an item in front of entry n, */
  276.     /* then you call this with (n, true) and entry n becomes n+1, n+1 becomes  */
  277.     /* n+2, etc. If you want to delete entry n, you delete and zero the SMAP   */
  278.     /* entry, and then call this with either n or n+1 and "false". Entry n+1   */
  279.     /* will become entry n, entry n+2 will become n+1, etc.                       */
  280.     
  281.     smapHand    theSmapHand;
  282.     OSErr        err;
  283.     short        count;
  284.     char        *entryPtr;
  285.     
  286.     if ((startValue <= 0) || (startValue >= 256))
  287.         return errAENoSuchObject;
  288.     theSmapHand = (smapHand)Get1Resource('SMAP', 0);    /* The SMAP is the index to the resources     */
  289.     if (theSmapHand == 0L) return resNotFound;            /* contained within the scrapbook file.     */
  290.     if ((err = ResError()) != noErr) return err;
  291.  
  292.     for (count = 0, entryPtr = (char *)*theSmapHand; count <= 255; count++, entryPtr++)
  293.         if (*entryPtr >= startValue) {
  294.             if (adjustUpwards)
  295.                 (*entryPtr) += 1;
  296.             else
  297.                 (*entryPtr) -= 1;
  298.         }
  299.     ChangedResource((Handle)theSmapHand);
  300.     if ((err = ResError()) != noErr) return err;
  301.     WriteResource((Handle)theSmapHand);
  302.     err = ResError();
  303.     
  304.     return err;
  305. }
  306.  
  307.  
  308. OSErr ZeroScrapbookIndex(short smapIndex)
  309. {
  310.     /* This is called when all of the elements of th item at "smapIndex" have been    */
  311.     /* deleted. It removes that item's entry from the scrapbook map.                */
  312.     smapHand    theSmap;
  313.     OSErr        err;
  314.     
  315.     theSmap = (smapHand)Get1Resource('SMAP', 0);    /* The SMAP is the index to the resources     */
  316.     if (theSmap == 0L) return resNotFound;            /* contained within the scrapbook file.     */
  317.     if ((err = ResError()) != noErr) return err;
  318.  
  319.     (**theSmap)[smapIndex] = 0;
  320.     
  321.     ChangedResource((Handle)theSmap);
  322.     err = ResError();
  323.     
  324.     return err;
  325. }
  326.  
  327.  
  328. OSErr Delete1ScrapbookItem (short entryIndex, OSType itemType, Boolean *entryRemoved)
  329. {
  330.     /* Delete the item of the given type from the given scrapbook entry     */
  331.     /* If itemType == typeWildCard, then all entries are deleted for that     */
  332.     /* entry.                                                                */
  333.     ResType        oneType;
  334.     short        resID, smapIndex, numTypes, typeIndex;
  335.     Handle        dummyHandle;
  336.     OSErr        err;
  337.  
  338.             
  339.     numTypes = Count1Types();     /* We'll use this to 1) scan all types on a "delete all" */
  340.                                 /* request and 2) scan all types to see if any entries   */
  341.                                 /* remain on a "delete 1 type" request.                     */
  342.                                 
  343.     *entryRemoved = false;
  344.     err = _EntryToScrapIndex(entryIndex, &smapIndex, &resID);
  345.     if (err == noErr) {
  346.         /* If we've been asked for a specific type, look for it */
  347.         if (itemType != typeWildCard) {
  348.             err = _Delete1ScrapItem(resID, itemType);
  349.             if (err != noErr) return err;
  350.             
  351.             /* Now, see if no more resources exist for this scrapbook entry */
  352.             /* If so, we can zero the corresponding entry in the resource    */
  353.             /* map.                                                            */
  354.             SetResLoad(false);    /* We just want to know if the resources exist */
  355.             for (typeIndex = 1; typeIndex <= numTypes; typeIndex++) {
  356.                 Get1IndType(&oneType, typeIndex);
  357.                 if ((err = ResError()) != noErr) break;
  358.                 (void) Get1Resource(oneType, resID); /* (void) == ignore the returned handle */
  359.                 if ((err = ResError()) != resNotFound) break;
  360.                      /* That's not a typo -- Exit the loop if a */
  361.                      /* resource was found or some other error  */
  362.                      /* occurred.                                */
  363.             }
  364.             SetResLoad(true);
  365.             if (err == resNotFound) {
  366.                 /* No resources were found, so we can remove this entry */
  367.                 err = ZeroScrapbookIndex(smapIndex);
  368.                 if (err != noErr) return err;
  369.                 err = AdjustScrapbookEntries(entryIndex, false);
  370.                 *entryRemoved = true;
  371.             }
  372.         }
  373.         
  374.         else {
  375.             /* The user asked for everything in this item, so we'll look through all */
  376.             /* available types for items with this resource number.                     */
  377.             for (typeIndex = 1; typeIndex <= numTypes; typeIndex++) {
  378.                 Get1IndType(&oneType, typeIndex);
  379.                 if ((err = ResError()) != noErr) break;
  380.                 SetResLoad(false);
  381.                 dummyHandle = Get1Resource(oneType, resID);
  382.                 SetResLoad(true);
  383.                 if (dummyHandle != NULL) {
  384.                     err = _Delete1ScrapItem(resID, oneType);
  385.                     if (err != noErr) return err;
  386.                 }
  387.             }
  388.             /* Since we deleted all of this items, remove this entry */
  389.             if (err == noErr) {
  390.                 *entryRemoved = true;
  391.                 err = ZeroScrapbookIndex(smapIndex);
  392.                 if (err != noErr) return err;
  393.                 err = AdjustScrapbookEntries(entryIndex, false);
  394.             }
  395.         }
  396.     }
  397.     return err;
  398. } /* Delete1ScrapbookItem */
  399.  
  400.  
  401. /* Since the "best type" for a given scrapbook entry varies depending on */
  402. /* what's in that entry, we've written the following routine to find the */
  403. /* best type for a given entry. The algorithm is as follows:             */
  404. /*   1) Search for elements of a particular type, in descending order     */
  405. /*      of preference.                                                     */
  406. /*   2) If we find something, return that item's type code. If we          */
  407. /*         don't, then return the type code for the first item.             */
  408.  
  409. OSErr GetBestType(short entryNum, ResType *bestType)
  410. {
  411.     OSErr        err            = noErr;
  412.     ResType        preferredTypes[3] = {'PICT', 'TEXT', 'snd '};
  413.     short        scrapIndex, resID, scratch;
  414.     Handle        dummyHandle;
  415.     short        count;
  416.     
  417.     err = _EntryToScrapIndex(entryNum, &scrapIndex, &resID);
  418.     if (err != noErr) return err;
  419.  
  420.     /* First, we'll try for one of our preferred types */
  421.     SetResLoad(false);    /* We only want to know if particular resources exist, */
  422.                         /* but we don't want to load them here.                   */
  423.     for (count = 0; count <= 2; count++) {
  424.         dummyHandle = Get1Resource(preferredTypes[count], resID);
  425.         if (dummyHandle != NULL) {
  426.             /* This resource exists, so return its type code */
  427.             *bestType = preferredTypes[count];
  428.             SetResLoad(true);
  429.             return noErr;
  430.         }
  431.     }
  432.     SetResLoad(true);
  433.     
  434.     /* If the above test fails, then we don't have a recognized type, so we'll */
  435.     /* take the first type we can get.                                           */
  436.     scratch = 1;
  437.     return GetAllTypes(entryNum, &scratch, bestType);
  438. }
  439.  
  440. /* Get a list of all of the resource types contained in entry "entryNum" */
  441. /* On entry, typeList is the address of an array of resource types, and  */
  442. /* numSlots is the number of openings in that array. On exit, numSlots     */
  443. /* is the number of type codes that were placed in the array.             */
  444.  
  445. OSErr    GetAllTypes(short entryNum, short *numSlots, ResType *typeList)
  446. {
  447.     short        lastSlot = *numSlots;
  448.     OSErr        err;
  449.     short        scrapIndex, resID;
  450.     short        numTypes, typeCount;
  451.     ResType        typeCode;
  452.     Handle        dummyHandle;
  453.     
  454.     *numSlots = 0;
  455.     err = _EntryToScrapIndex(entryNum, &scrapIndex, &resID);
  456.     if (err != noErr) return err;
  457.  
  458.     numTypes = CountTypes();
  459.     SetResLoad(false);    /* We only want to know if particular resources exist, */
  460.                         /* but we don't want to load them here.                   */
  461.     for (typeCount = 1; typeCount <= numTypes; typeCount++) {
  462.         Get1IndType(&typeCode, typeCount);
  463.         if ((err = ResError()) != noErr) break;
  464.         dummyHandle = Get1Resource(typeCode, resID);
  465.         if (dummyHandle != NULL) {
  466.             /* This resource exists, so add it to the list */
  467.             typeList[*numSlots] = typeCode;
  468.             ++(*numSlots);
  469.             if (*numSlots >= lastSlot) break;    /* Exit this loop */
  470.         }
  471.     }
  472.     SetResLoad(true);
  473.         
  474.     return err;
  475. }
  476.  
  477.     
  478.